package com.fengycx.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fengycx.constants.Constants;
import com.fengycx.constants.GlobalExceptionCode;
import com.fengycx.dto.SearchDriverDto;
import com.fengycx.exception.GlobalException;
import com.fengycx.feign.CreateOrderApi;
import com.fengycx.feign.CustomerFineApi;
import com.fengycx.feign.DriverConfirmFeeApi;
import com.fengycx.feign.DriverHbaseGeoApi;
import com.fengycx.mapper.OrderMapper;
import com.fengycx.param.*;
import com.fengycx.pojo.domain.LoginContext;
import com.fengycx.pojo.domain.Order;
import com.fengycx.pojo.domain.OrderBill;
import com.fengycx.pojo.domain.OrderProfitsharing;
import com.fengycx.pojo.dto.CreateOrderDto;
import com.fengycx.pojo.dto.OderBillDto;
import com.fengycx.pojo.dto.Order2DriverDto;
import com.fengycx.pojo.vo.OrderDetailVo;
import com.fengycx.pojo.vo.PointVo;
import com.fengycx.result.CallDriverCalculatedBillResult;
import com.fengycx.result.R;
import com.fengycx.service.IOrderBillService;
import com.fengycx.service.IOrderProfitsharingService;
import com.fengycx.service.IOrderService;
import com.fengycx.temlate.RedisGeoTemplate;
import com.fengycx.utils.AssertUtil;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.tomcat.util.bcel.Const;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;



/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author Cino
 * @since 2024-03-09
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private CreateOrderApi createOrderApi;
    @Autowired
    private RedisGeoTemplate redisGeoTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IOrderBillService orderBillService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private CustomerFineApi customerFineApi;
    @Autowired
    private DefaultMQPullConsumer defaultMQPullConsumer;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private DriverHbaseGeoApi driverHbaseGeoApi;
    @Autowired
    private DriverConfirmFeeApi driverConfirmFeeApi;
    @Autowired
    private IOrderProfitsharingService orderProfitsharingService;

    /**
     * 司机发送账单
     * @param orderNo
     */
    @Override
    public void driverSendPayOrder(String orderNo) {
        //JSR 303校验
        //业务校验
        //账单是否存在，并且账单状态是否为5
        Order order = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getStatus, Constants.Order.DRIVER_CONFIRMATION_FEE)
                .eq(Order::getOrderNo,orderNo));
        AssertUtil.isNotNull(order,"司机发送账单：未找到符合条件的订单！");
        //账单是否为司机本人的
        AssertUtil.isEquals(order.getDriverId(),StpUtil.getLoginIdAsLong(),GlobalExceptionCode.SERVICE_ERROR);
        //修改订单状态为6
        order.setStatus(Constants.Order.UNPAID);
        Date date = new Date();
        order.setUpdateTime(date);
        //组装消费者 pay服务 所需对象payOrder
        JSONObject object = new JSONObject();
        object.putOpt("payUserId",order.getCustomerId());
        object.putOpt("orderNo",orderNo);
        object.putOpt("amount",order.getRealOrderAmount());
        object.putOpt("payType", Constants.PayType.WECHAT);
        //组装消息 （一会看一下 Message是个啥）TODO
        Message<JSONObject> message = MessageBuilder.withPayload(object).build();

        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction("order-to-pay-group", "order-topic:order-to-pay-tag", message, order);
        AssertUtil.isEquals(transactionSendResult.getSendStatus().toString(),SendStatus.SEND_OK.toString(),GlobalExceptionCode.SERVICE_ERROR);



    }


    /**
     * 司机查询账单
     * @param orderNo
     * @return
     */
    @Override
    public OderBillDto payOrderSend(String orderNo) {
        //JSR 303校验
        //业务校验
        Order order = selectOrderByOrderNo(orderNo);
        OrderBill orderBill = orderBillService.getOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderNo, order));
        AssertUtil.isNotNull(orderBill,GlobalExceptionCode.SERVICE_ERROR);
        //分成是否存在
        OrderProfitsharing orderProfitsharing = orderProfitsharingService.getOne(new LambdaQueryWrapper<OrderProfitsharing>().eq(OrderProfitsharing::getOrderNo, orderNo));
        AssertUtil.isNotNull(orderProfitsharing,GlobalExceptionCode.SERVICE_ERROR);

        //把相关信息拷贝到OrderBillDto中
        OderBillDto oderBillDto = BeanUtil.copyProperties(order, OderBillDto.class);
        BeanUtil.copyProperties(orderBill,oderBillDto);
        BeanUtil.copyProperties(orderProfitsharing,oderBillDto);
        return oderBillDto;
    }

    /**
     * 司机确认费用接口
     * @param confirmDto
     */
    @Override
    public void driverConfirmOrder(DriverConfirmOrderDto confirmDto) {
        //JSR 303校验参数
        //校验订单是否存在
        String orderNo = confirmDto.getOrderNo();
        Order order = selectOrderByOrderNo(orderNo);
        AssertUtil.isNotNull(order,"确认账单的订单不存在！");
        AssertUtil.isEquals(order.getStatus(),Constants.Order.END_DRIVING,GlobalExceptionCode.SERVICE_ERROR);
        //账单是否为司机本人
        AssertUtil.isEquals(order.getDriverId(),StpUtil.getLoginIdAsLong(),GlobalExceptionCode.SERVICE_ERROR);

        //3.业务实现
        //3.1.1 修改账单信息
        OrderBill orderBill = orderBillService.getOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderNo, orderNo));
        AssertUtil.isNotNull(orderBill,GlobalExceptionCode.SERVICE_ERROR);
        BigDecimal tollFee = confirmDto.getTollFee();
        BigDecimal parkingFee = confirmDto.getParkingFee();
        BigDecimal otherFree = confirmDto.getOtherFree();
        orderBill.setParkingAmount(parkingFee);
        orderBill.setOtherAmount(otherFree);
        orderBill.setTollAmount(tollFee);
        Date date = new Date();
        orderBill.setUpdateTime(date);
        orderBillService.updateById(orderBill);

        //修改订单信息
        BigDecimal totalAmount = order.getRealOrderAmount().add(parkingFee).add(otherFree).add(tollFee);
        order.setRealOrderAmount(totalAmount);
        order.setStatus(Constants.Order.DRIVER_CONFIRMATION_FEE);
        order.setUpdateTime(date);
        super.updateById(order);

        //获得司机summary,获得司机取消数，投诉数
        R<DriverSummeryDto> driverSummeryDtoR = driverConfirmFeeApi.selectDriverSummary();
        AssertUtil.isTrue(driverSummeryDtoR.isSuccess(),GlobalExceptionCode.SERVICE_ERROR);
        DriverSummeryDto driverSummery = driverSummeryDtoR.getData();
        Integer todayCancel = driverSummery.getTodayCancel();
        Integer todayComplaint = driverSummery.getTodayComplaint();
        //根据取消数、投诉数、总金额、计算分账比例、金额信息
        SharedAmountDto sharedAmountDto = new SharedAmountDto();
        sharedAmountDto.setTodayCancel(todayCancel);
        sharedAmountDto.setTodayComplaint(todayComplaint);
        sharedAmountDto.setTotalAmount(totalAmount);

        R<SharedAmountResult> sharedAmountResultR = createOrderApi.sharedAmount(sharedAmountDto);
        SharedAmountResult amountResult = sharedAmountResultR.getData();
        AssertUtil.isTrue(sharedAmountResultR.isSuccess(),GlobalExceptionCode.SERVICE_ERROR);
        //保存分账表
        OrderProfitsharing orderProfitsharing = BeanUtil.copyProperties(amountResult, OrderProfitsharing.class);
        orderProfitsharing.setOrderId(order.getId());
        orderProfitsharing.setOrderNo(orderNo);
        orderProfitsharing.setOrderAmount(totalAmount);
        orderProfitsharing.setStatus(0);
        String loginInfoKey = String.format(Constants.Login.LOGIN_CONTEXT_KEY, StpUtil.getLoginIdAsString());
        Object loginInfo = redisTemplate.opsForValue().get(loginInfoKey);
        AssertUtil.isNotNull(loginInfo,GlobalExceptionCode.SERVICE_ERROR);
        LoginContext loginContext = JSONUtil.toBean(JSONUtil.toJsonStr(loginInfo), LoginContext.class);
        orderProfitsharing.setToUserOpenId(loginContext.getOpenId());
        orderProfitsharingService.save(orderProfitsharing);
    }



    /**
     * 查询订单通用接口
     * @param orderNo
     */
    @Override
    public Order orderInfo(String orderNo) {
        Order order = selectOrderByOrderNo(orderNo);
        return order;
    }

    /**
     * 前端实时拉取司机坐标位置信息
     * @param orderNo
     * @return
     */
    @Override
    public PointVo pullDriverLocation(String orderNo) {
        Order order = selectOrderByOrderNo(orderNo);
        String driverId = order.getDriverId().toString();
        //key:(member:xy)
        List<Point> points = redisTemplate.opsForGeo().position(Constants.Redis.DRIVER_LOCATION_KEY, driverId);
        AssertUtil.isTrue(CollUtil.isNotEmpty(points),"未查询到司机坐标");
        Point point = points.get(0);
        return new PointVo( String.valueOf(point.getX()),String.valueOf(point.getY()));
    }

    /**
     * 乘客司乘同显页面获取订单详情
     * @param orderNo
     * @return
     */
    @Override
    public OrderDetailVo getOrderDetail(String orderNo) {
        Order order = selectOrderByOrderNo(orderNo);
        OrderBill orderBill = orderBillService.getOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderNo, orderNo));
        AssertUtil.isNotNull(orderBill,"OrderBill不存在，请联系管理员");
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        BeanUtil.copyProperties(order,orderDetailVo);
        BeanUtil.copyProperties(orderBill,orderDetailVo);

        return orderDetailVo;
    }



    /**
     * 司机抢单
     * @param orderNo
     * @return
     */
    @Override
    public Order grabOrder(String orderNo) {
        //p判断数据库中是否有订单
        String orderKey = String.format(Constants.Redis.ORDER_INFO_KEY, orderNo);
        Object obj = redisTemplate.opsForValue().get(orderKey);
        AssertUtil.isNotNull( obj,"手速慢了！");
        Order order = JSONUtil.toBean(JSONUtil.toJsonStr(obj),Order.class);


            //查询到 则加锁 防止每个订单锁的key一致，故拼接字符串
            String lockKey = String.format(Constants.Redis.ORDER_LOCK_IN_REDIS_KEY, orderNo);
            RLock rLock = redissonClient.getLock(lockKey);
            //阻塞式锁
            // rLock.lock();
            //非阻塞式，没抢到就是false ，这里使用非阻塞式锁，不用双重校验，仅需校验锁内逻辑即可
            boolean isLock = rLock.tryLock();
            //这里通过则代表抢到了
            AssertUtil.isTrue(isLock,GlobalExceptionCode.SERVICE_ERROR);
            //修改订单信息到数据库
        try {
            long driverId = StpUtil.getLoginIdAsLong();
            String loginContextKey = String.format(Constants.Login.LOGIN_CONTEXT_KEY, driverId);
            Object contextObj = redisTemplate.opsForValue().get(loginContextKey);
            LoginContext driverLoginContext = JSONUtil.toBean(JSONUtil.toJsonStr(contextObj), LoginContext.class);
            AssertUtil.isNotNull(driverLoginContext,"未查询到登录信息，请先登录");
            Date date = new Date();
            order.setUpdateTime(date);
            order.setStatus(Constants.Order.RECEIVED_ORDER);
            order.setAcceptTime(date);
            order.setDriverId(driverId);
            order.setDriverName(driverLoginContext.getName());
            order.setDriverPhone(driverLoginContext.getPhone());
            order.setDriverPhoto(driverLoginContext.getAvatar());
            super.updateById(order);
            //删除redis中待抢的订单信息
            redisTemplate.delete(orderKey);
            return order;
        } finally {
            //释放锁，不然其他线程一直拿不到
            if (rLock.isLocked()){
                rLock.unlock();
            }
        }
    }

    /**
     * 回显司机抢单成功后的工作台订单详情
     * @return
     */
    @Override
    public Order reshowProcessingOrder() {
        long driverId = StpUtil.getLoginIdAsLong();
        return super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getDriverId, driverId)
                .in(Order::getStatus, Constants.Order.DRIVER_ORDER_IN_PROGRESS)
        );
    }

    /**
     * 到达代驾点
     * @return
     */
    @Override
    public Order arriveStartPlaceHandle() {
        Order order = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getDriverId, StpUtil.getLoginIdAsLong())
                .eq(Order::getStatus, Constants.Order.RECEIVED_ORDER)
        );
        AssertUtil.isNotNull(order,"未查询到订单，请联系管理员");
        order.setStatus(Constants.Order.DRIVER_ARRIVED);
        Date date = new Date();
        order.setUpdateTime(date);
        order.setArriveTime(date);
        super.updateById(order);
        return order;
    }

    /**
     * 开始代驾
     * @return
     */
    @Override
    public Order startDrivingHandle() {
        long driverId = StpUtil.getLoginIdAsLong();
        //当前司机下，已到达代驾点的订单
        Order order = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getDriverId,driverId)
                .eq(Order::getStatus, Constants.Order.DRIVER_ARRIVED)
        );
        AssertUtil.isNotNull(order,"到达代驾点订单不存在，请联系管理员");
        OrderBill orderBill = orderBillService.getOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderId, order.getId()));
        AssertUtil.isNotNull(orderBill,"到达代驾点订单明细不存在，请联系管理员");

        Date startDrivingTime = new Date();
        order.setStatus(Constants.Order.START_DRIVING);
        order.setUpdateTime(startDrivingTime);
        order.setStartTime(startDrivingTime);
        Date arriveTime = order.getArriveTime();
        Long waitTime = DateUtil.between(arriveTime, startDrivingTime, DateUnit.MINUTE);
        //等待时长
        orderBill.setWaitingMinute(waitTime.intValue());
        //超出免费等待时间 则计算费用
        if(waitTime.compareTo(orderBill.getFreeBaseWaitingMinute().longValue())>0){
            BigDecimal overWaitTime = BigDecimal.valueOf(waitTime - orderBill.getFreeBaseWaitingMinute().longValue());
            orderBill.setWaitingAmount(orderBill.getExceedEveryMinuteAmount().multiply(overWaitTime));
        }

        super.updateById(order);
        orderBillService.updateById(orderBill);
        return order;
    }

    /**
     * 结束代驾接口
     * @return
     */
    @Override
    public Order endDrivingHandle() {
        long driverId = StpUtil.getLoginIdAsLong();
        Order order = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getDriverId, driverId)
                .eq(Order::getStatus,Constants.Order.START_DRIVING));
        AssertUtil.isNotNull(order,"结束代驾订单不存在，请联系管理员");
        OrderBill orderBill = orderBillService.getOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderNo, order.getOrderNo()));
        order.setStatus(Constants.Order.END_DRIVING);
        Date date = new Date();
        order.setUpdateTime(date);
        order.setEndTime(date);
        //获取订单实际里程 调用大数据数据库 获取司机坐标集合
        List<PointVo> driverGeoList = driverHbaseGeoApi.getDriverGeoList(order.getOrderNo());
        AssertUtil.isTrue(CollUtil.isNotEmpty(driverGeoList),"司机坐标集合为空！");
        //循环调用腾讯画线API，计算真实里程
        BigDecimal realMileageKM= getRealDriverMileage(driverGeoList);

        //根据实际里程计算价格
        CalculateResultParam calculateResultParam = new CalculateResultParam();
        calculateResultParam.setCurrentTime(date);
        calculateResultParam.setDistance(realMileageKM);
        calculateResultParam.setWaitTime(orderBill.getWaitingMinute());

        //根据实际里程计算价格
        R<CallDriverCalculatedBillResult> calculateResult = createOrderApi.getCallDriverCalculatedBillResult(calculateResultParam);
        AssertUtil.isTrue(calculateResult.isSuccess(),"计算价格失败，请联系管理员！");
        CallDriverCalculatedBillResult priceResult = calculateResult.getData();

        //将实际价格信息设置到order、orderBill
        order.setRealMileage(realMileageKM);
        BigDecimal expectsOrderAmount = priceResult.getExpectsOrderAmount();
        order.setRealOrderAmount(expectsOrderAmount);
        order.setReturnMileage(realMileageKM);

        orderBill.setRealOrderAmount(expectsOrderAmount);
        orderBill.setMileageAmount(priceResult.getMileageAmount());
        orderBill.setWaitingAmount(priceResult.getWaitingAmount());

        super.updateById(order);
        orderBillService.updateById(orderBill);
        return order;
    }

    /**
     * 调用腾讯地图API 用经纬度集合获取真实路线里程
     * @param driverGeoList
     * @return
     */
    private BigDecimal getRealDriverMileage(List<PointVo> driverGeoList) {
        String url="https://apis.map.qq.com/ws/direction/v1/driving/?from=%s&to=%s&key=OCMBZ-IC5CQ-HTE5L-B2AVM-UN4CK-GLFI6";
        BigDecimal transform = new BigDecimal(0.001);
        BigDecimal mileagePart = BigDecimal.ZERO;
        for (int i = 0; i < driverGeoList.size()-1; i++) {
            PointVo from = driverGeoList.get(i);
            PointVo to = driverGeoList.get(i + 1);
            String request = String.format(url, from.getLatitude() + "," + from.getLongitude(), to.getLatitude() + "," + to.getLongitude());
            String res = HttpUtil.get(request);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {

            }
            //请求结果
            JSONObject resObj = JSONUtil.parseObj(JSONUtil.toJsonStr(res));
            //调用地图api是否成功
            AssertUtil.isEquals(resObj.get("status").toString(),"0",GlobalExceptionCode.SERVICE_ERROR);
            String distance = JSONUtil.parseObj(resObj.getJSONObject("result").getJSONArray("routes").get(0)).get("distance").toString();
            mileagePart = new BigDecimal(distance).add(mileagePart);
        }
        BigDecimal realMileageKM = mileagePart.multiply(transform);
        return realMileageKM;
    }

    /**
     * 司机定时拉取订单列表
     *
     *  *客户创建订单时 发送同步消息到RocketMQ中，
     *  *在此直接进行消费
     *
     * @return
     */
    @Override
    public List<Order2DriverDto> pullOrder2Driver() {
        //driver loginId
        String loginId = StpUtil.getLoginIdAsString();
        try {
            //"push-order-topic:to-driver-tag" + driverId
            //订阅消息队列，指定topic
            Set<MessageQueue> messageQueues = defaultMQPullConsumer.fetchSubscribeMessageQueues("push-order-topic");
            //封装结果
            ArrayList<Order2DriverDto> result = new ArrayList<>();
            for (MessageQueue messageQueue : messageQueues) {
                //通过offset偏移量来获取消息，当前队列的消费位置 fromStore:控制消费者在启动时从哪里获取消费进度信息,表示是否从存储中获取消费进度
                long offset = defaultMQPullConsumer.fetchConsumeOffset(messageQueue, true);
                //拉取消息，需要指定tags拼接司机Id，来区分司机， maxNums：拉取消息数量
                PullResult pullResult = defaultMQPullConsumer.pull(messageQueue, "to-driver-tag" + loginId, offset, 5);
                //找到消息
                if(pullResult!=null && pullResult.getPullStatus().equals(PullStatus.FOUND)){
                    //拿到消息列表
                    List<MessageExt> msgFoundList = pullResult.getMsgFoundList();
                    if(msgFoundList==null || msgFoundList.size()==0)continue;

                    for (MessageExt messageExt : msgFoundList) {
                        //拿到消息内容，转换为订单对象
                        String message = new String(messageExt.getBody(), StandardCharsets.UTF_8);
                        Order2DriverDto order = JSON.parseObject(message, Order2DriverDto.class);
                        //添加到结果列表
                        result.add(order);
                        log.info("获取订单消息 {}",order);

                    }
                }
                if(pullResult !=null){
                    //重要：修改消息的消费位置，如果位置不后移，消息会一直被消费
                    defaultMQPullConsumer.updateConsumeOffset(messageQueue,pullResult.getNextBeginOffset());
                }
                if (result.size() >4) {
                    break;
                }
            }
            return result;
        } catch (Exception e) {
            throw new GlobalException("拉取订单失败！");
        }
    }

    /**
     * 创建订单
     * @param createOrderParam
     * @return
     */
    @Override
    public String createOrder(CreateOrderDto createOrderParam) {
        //参数校验 -JSR303
        //业务校验
        //当前用户是否有正在进行的订单
        long customerId = StpUtil.getLoginIdAsLong();
        Order order= super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getCustomerId, customerId)
                .in(Order::getStatus, Constants.Order.CUSTOMER_ORDER_IN_PROGRESS)
        );
        AssertUtil.isNull(order, "客户存在正在进行的账单！");
        //雪花算法
        String orderNo= IdUtil.fastSimpleUUID();
        //计算价格
        CalculateResultParam calculateResultParam = new CalculateResultParam();
        calculateResultParam.setCurrentTime(new Date());
        BigDecimal expectsMileage = createOrderParam.getExpectsMileage();
        calculateResultParam.setDistance(expectsMileage);

        R<CallDriverCalculatedBillResult> callDriverCalculatedBillResult = createOrderApi.getCallDriverCalculatedBillResult(calculateResultParam);
        AssertUtil.isTrue(callDriverCalculatedBillResult.isSuccess(),GlobalExceptionCode.SERVICE_ERROR);
        //获取查找后的司机集合
        CallDriverCalculatedBillResult data = callDriverCalculatedBillResult.getData();
        //查找上线的司机
        List<SearchDriverDto> searchDriverResult = redisGeoTemplate.searchDriverGeo(Constants.Redis.DRIVER_LOCATION_KEY,
                Double.valueOf(createOrderParam.getStartPlaceLongitude()),
                Double.valueOf(createOrderParam.getStartPlaceLatitude()));

        //根据司机配置查找符合条件的司机
        List<SearchDriverDto> filteredSearchDriverResultList = searchDriverResult.stream().filter(sd -> {
            //获取司机id
            Long driverId = sd.getDriverId();
            //获取司机到乘客的距离
            Double distance = sd.getDistance();
            //拿到司机存储到redis中的setting配置
            String key = String.format(Constants.Redis.DRIVER_ONLINE_KEY, driverId);
            Object driverSetting = redisTemplate.opsForValue().get(key);
            //判断司机配置，如果不为空则进一步过滤
            if (ObjectUtil.isNotNull(driverSetting)) {
                JSONObject driverSettingJSONObj = JSONUtil.toBean(JSONUtil.toJsonStr(driverSetting), JSONObject.class);
                //大于司机自己设置的接单范围 则过滤出来
                if (distance.compareTo(driverSettingJSONObj.getDouble("rangeDistance")) > 0) {
                    return false;
                }
                if (expectsMileage.compareTo(driverSettingJSONObj.getBigDecimal("orderDistance")) > 0) {
                    return false;
                }

                //使用 RocketMQ 推送订单给司机，发送消息类型为 同步消息
                Order2DriverDto order2DriverDto = new Order2DriverDto();

                order2DriverDto.setDistance(distance);
                order2DriverDto.setExpectsMileage(expectsMileage);
                order2DriverDto.setFavourFee(BigDecimal.ZERO);
                order2DriverDto.setExpectsOrderAmount(data.getExpectsOrderAmount());
                order2DriverDto.setStartPlace(createOrderParam.getStartPlace());
                order2DriverDto.setEndPlace(createOrderParam.getEndPlace());
                order2DriverDto.setOrderNo(orderNo);
                //组装消息
                Message<Order2DriverDto> message = MessageBuilder.withPayload(order2DriverDto).build();
                //发送结果
                SendResult sendResult = rocketMQTemplate.syncSend("push-order-topic:to-driver-tag" + driverId, message);
                //destination: topic:tag
                if(SendStatus.SEND_OK.equals(sendResult.getSendStatus())){
                    log.error("给司机推送订单失败，司机id为:{},MQ发送结果为:{}",driverId,sendResult.getSendStatus());
                }
                return true;
            }
            return false;
        }).toList();

        if( ArrayUtil.isEmpty(filteredSearchDriverResultList)){
            throw new GlobalException("无符合条件的司机！");
        }
        //创建订单
        order= createOrderResult(data,createOrderParam,orderNo);
        //创建订单明细
        createBill(order,data);

        //使用mq发送延迟消息，如果用户60s 创建定订单超时 则自动取消
        //组装消息
        Message<String> message = MessageBuilder.withPayload(orderNo).build();
        SendResult result = rocketMQTemplate.syncSend("order-topic:delay-cancel-tag", message,3000,5);
        if(!SendStatus.SEND_OK.equals(result.getSendStatus()) ){
            log.error("取消订单发送延迟消息失败，订单号为：{}",orderNo);
        }
        //将订单对象存入Redis中，供司机抢单
        String orderKey = String.format(Constants.Redis.ORDER_INFO_KEY, orderNo);
        redisTemplate.opsForValue().set(orderKey,order);

        return orderNo;
    }

    /**
     * 取消订单实现类
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) {
        //参数校验 -JSR303
        //2.业务校验
        //2.1.订单是否存在
        Order order= selectOrderByOrderNo(orderNo);
        //2.2.当前订单是否属于当前登录人
        AssertUtil.isEquals( StpUtil.getLoginIdAsLong(),order.getCustomerId(),GlobalExceptionCode.SERVICE_ERROR);
        //2.3.订单状态是否是0、1、2
        Integer status = order.getStatus();
        AssertUtil.isTrue(Constants.Order.ORDER_CANCELABLE.contains(status),GlobalExceptionCode.SERVICE_ERROR);

        //3.业务实现
        //3.1修改订单状态
        order.setStatus(Constants.Order.CUSTOMER_CANCEL);
        order.setUpdateTime(new Date());
        super.updateById(order);
        //3.2保存乘客罚款，如果订单状态为2那么罚预估金额的20%，
        BigDecimal fineAmount =null;
        BigDecimal expectsOrderAmount=order.getExpectsOrderAmount();
        //订单状态为1罚预估金额的10%
        if(status.equals(Constants.Order.RECEIVED_ORDER)){
            fineAmount= expectsOrderAmount.multiply(new BigDecimal("0.1"));
        }else if(status.equals(Constants.Order.DRIVER_ARRIVED)){
            fineAmount= expectsOrderAmount.multiply(new BigDecimal("0.2"));
        }

        if(ObjectUtil.isNotNull(fineAmount)){
            CustomerCancelOrderParam customerCancelOrderParam = new CustomerCancelOrderParam();
            customerCancelOrderParam.setCustomerId(StpUtil.getLoginIdAsLong());
            customerCancelOrderParam.setOrderId(order.getId());
            customerCancelOrderParam.setAmount(fineAmount);
            customerCancelOrderParam.setRemark("乘客取消订单，订单编号为："+order.getOrderNo());

            R<Void> result = customerFineApi.customerCancelOrder(customerCancelOrderParam);
            AssertUtil.isTrue(result.isSuccess(),GlobalExceptionCode.SERVICE_ERROR);
        }
        //3.3rocketmq删除消息

    }

    /**
     * 查询正在进行中的订单
     * @return
     */
    @Override
    public Order queryProcessingOrder() {
        long loginId = StpUtil.getLoginIdAsLong();
        Order processingOrder = super.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getCustomerId, loginId)
                .in(Order::getStatus, Constants.Order.CUSTOMER_ORDER_IN_PROGRESS)
        );
        return processingOrder;
    }

    /**
     * 根据订单编号查询订单
     */
    private Order selectOrderByOrderNo(String orderNo) {
        Order order = super.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, orderNo));
        AssertUtil.isNotNull(order,"订单不存在！");
        return order;
    }

    /**
     * 保存订单
     * @param data
     * @param createOrderParam
     * @return
     */
    private Order createOrderResult(CallDriverCalculatedBillResult data, CreateOrderDto createOrderParam,String orderNo) {
        Order order = new Order();
        //雪花算法
        order.setOrderNo(orderNo);
        long customerId = StpUtil.getLoginIdAsLong();
        order.setCustomerId(customerId);
        order.setStartPlace(createOrderParam.getStartPlace());
        order.setEndPlace(createOrderParam.getEndPlace());
        BigDecimal expectsMileage = createOrderParam.getExpectsMileage();
        order.setExpectsMileage(expectsMileage);
        order.setReturnMileage(expectsMileage);

        order.setExpectsOrderAmount(data.getExpectsOrderAmount());
        order.setFavourAmount(BigDecimal.ZERO);
        order.setIncentiveAmount(BigDecimal.ZERO);
        order.setRealOrderAmount(BigDecimal.ZERO);

        order.setCarPlate(createOrderParam.getCarPlate());
        order.setCarType(createOrderParam.getCarType());
        order.setStatus(Constants.Order.WAIT_ORDER);
        order.setExpectMinutes(createOrderParam.getExpectMinutes());
        order.setStartPlaceLongitude(createOrderParam.getStartPlaceLongitude());
        order.setStartPlaceLatitude(createOrderParam.getStartPlaceLatitude());
        order.setEndPlaceLongitude(createOrderParam.getEndPlaceLongitude());
        order.setEndPlaceLatiude(createOrderParam.getEndPlaceLatitude());
        order.setIncentiveFeeStatus(false);

        String key = String.format(Constants.Login.LOGIN_CONTEXT_KEY, customerId);
        LoginContext loginContext = JSONUtil.toBean(JSONUtil.toJsonStr(redisTemplate.opsForValue().get(key)), LoginContext.class);
        AssertUtil.isTrue(ObjectUtil.isNotNull(loginContext),"登录信息为空，请登录！");

        order.setCustomerName(loginContext.getNickname());
        order.setCustomerPhone(loginContext.getPhone());
        order.setCustomerPhoto(loginContext.getAvatar());
        order.setCreateTime(new Date());

        super.save(order);
        return order;
    }

    /**
     * 保存订单明细
     * @param order
     * @param data
     */
    private void createBill(Order order, CallDriverCalculatedBillResult data) {
        OrderBill orderBill = new OrderBill();
        orderBill.setOrderId(order.getId());
        orderBill.setRealOrderAmount(new BigDecimal("0"));
        orderBill.setRealPayAmount(new BigDecimal("0"));
        orderBill.setTollAmount(new BigDecimal("0"));
        orderBill.setFavourAmount(new BigDecimal("0"));
        orderBill.setIncentiveAmount(new BigDecimal("0"));
        orderBill.setVoucherAmount(new BigDecimal("0"));
        orderBill.setOrderDetail("");

        orderBill.setBaseMileage(data.getBaseMileage());
        orderBill.setBaseMileageAmount(data.getBaseMileageAmount());
        orderBill.setExceedBaseMileageAmount(data.getExceedBaseMileageAmount());
        orderBill.setMileageAmount(data.getMileageAmount());
        orderBill.setFreeBaseWaitingMinute(data.getFreeBaseWaitingMinute());
        orderBill.setWaitingAmount(data.getWaitingAmount());
        orderBill.setFreeBaseReturnMileage(data.getFreeBaseReturnMileage());
        orderBill.setExceedBaseReturnEveryKmAmount(data.getExceedBaseReturnEveryKmAmount());
        orderBill.setExceedEveryMinuteAmount(data.getExceedEveryMinuteAmount());
        orderBill.setReturnAmont(data.getReturnAmont());
        orderBill.setOrderNo(order.getOrderNo());
        orderBill.setOtherAmount(BigDecimal.ZERO);

        orderBill.setParkingAmount(new BigDecimal("0"));
        orderBill.setCreateTime(new Date());
        orderBillService.save(orderBill);

    }


}
